/*
* Author: Chris Seguin
*
* This software has been developed under the copyleft
* rules of the GNU General Public License. Please
* consult the GNU General Public License for more
* details about use and distribution of this software.
*/
package org.acm.seguin.summary;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import org.acm.seguin.parser.ast.ASTTypeDeclaration;
import org.acm.seguin.parser.ast.SimpleNode;
import org.acm.seguin.parser.factory.BufferParserFactory;
import org.acm.seguin.parser.factory.FileParserFactory;
import org.acm.seguin.parser.factory.InputStreamParserFactory;
import org.acm.seguin.parser.factory.ParserFactory;
/**
* Stores a summary of a java file
*
*@author Chris Seguin
*@created June 6, 1999
*/
public class FileSummary extends Summary {
// Instance Variables
private File theFile;
private ArrayList importList;
private LinkedList typeList;
private boolean isMoving;
private boolean delete;
private Date lastModified;
// Class Variables
private static HashMap fileMap;
/**
* Creates a file map
*
*@param parentSummary the parent summary
*@param initFile the file
*/
protected FileSummary(Summary parentSummary, File initFile)
{
// Initialize parent class
super(parentSummary);
// Set instance Variables
theFile = initFile;
importList = null;
typeList = null;
isMoving = false;
delete = false;
lastModified = new Date();
}
/**
* Change whether this file is moving or not
*
*@param way the way that this parameter is changing
*/
public void setMoving(boolean way)
{
isMoving = way;
}
/**
* Mark whether this file should be deleted
*
*@param way the way that this parameter is changing
*/
public void setDeleted(boolean way)
{
delete = way;
}
/**
* Return the name of the file
*
*@return a string containing the name
*/
public String getName()
{
if (theFile == null) {
return "";
}
return theFile.getName();
}
/**
* Return the file
*
*@return the file object
*/
public File getFile()
{
return theFile;
}
/**
* Return the list of imports
*
*@return an iterator containing the imports
*/
public Iterator getImports()
{
if (importList == null) {
return null;
}
return importList.iterator();
}
/**
* Counts the types stored in the file
*
*@return the number of types in this file
*/
public int getTypeCount()
{
if (typeList == null) {
return 0;
}
return typeList.size();
}
/**
* Get the list of types stored in this file
*
*@return an iterator over the types
*/
public Iterator getTypes()
{
if (typeList == null) {
return null;
}
return typeList.iterator();
}
/**
* Is this file moving to a new package
*
*@return true if the file is moving
*/
public boolean isMoving()
{
return isMoving;
}
/**
* Has this file been deleted
*
*@return true if the file is deleted
*/
public boolean isDeleted()
{
return delete;
}
/**
* Provide method to visit a node
*
*@param visitor the visitor
*@param data the data for the visit
*@return some new data
*/
public Object accept(SummaryVisitor visitor, Object data)
{
return visitor.visit(this, data);
}
/**
* Description of the Method
*
*@return Description of the Returned Value
*/
public String toString()
{
if (theFile == null) {
return "FileSummary<Framework File>";
}
return "FileSummary<" + theFile.getName() + ">";
}
/**
* Add an import summary
*
*@param importSummary the summary of what was imported
*/
protected void add(ImportSummary importSummary)
{
if (importSummary != null) {
// Initialize the import list
if (importList == null) {
initImportList();
}
// Add it in
importList.add(importSummary);
}
}
/**
* Add an type summary
*
*@param typeSummary the summary of the type
*/
protected void add(TypeSummary typeSummary)
{
if (typeSummary != null) {
// Initialize the type list
if (typeList == null) {
initTypeList();
}
// Add it to the list
typeList.add(typeSummary);
}
}
/**
* Initialize the type list
*/
private void initTypeList()
{
typeList = new LinkedList();
}
/**
* Initialize the import list
*/
private void initImportList()
{
importList = new ArrayList();
}
/**
* Get the file summary for a particular file
*
*@param file the file we are looking up
*@return the file summary
*/
public static FileSummary getFileSummary(File file)
{
if (fileMap == null) {
init();
}
FileSummary result = (FileSummary) fileMap.get(getKey(file));
if (result == null) {
SummaryLoaderState state = loadNewFileSummary(file);
result = linkFileSummary(state, file);
}
else {
Date currentModificationTime = new Date(file.lastModified());
if (currentModificationTime.after(result.lastModified)) {
resetFileSummary(file, result);
result.lastModified = new Date(file.lastModified());
// Create a new file summary object
ParserFactory factory = new FileParserFactory(file);
SimpleNode root = factory.getAbstractSyntaxTree(false);
if (root == null) {
return null;
}
reloadFileSummary(file, result, root);
}
}
return result;
}
/**
* Get the file summary for a particular file
*
*@param buffer the buffer that is used to load the summary
*@return the file summary
*/
public static FileSummary getFileSummary(String buffer)
{
// Create a new file summary object
ParserFactory factory = new BufferParserFactory(buffer);
SimpleNode root = factory.getAbstractSyntaxTree(false);
if ((root == null) || (!hasType(root))) {
return null;
}
// Start the summary
SummaryLoaderState state = new SummaryLoaderState();
state.setFile(null);
root.jjtAccept(new SummaryLoadVisitor(), state);
// Associate them together
FileSummary result = (FileSummary) state.getCurrentSummary();
((PackageSummary) result.getParent()).addFileSummary(result);
return result;
}
/**
* Remove any file summaries that have been deleted
*/
public static void removeDeletedSummaries()
{
if (fileMap == null) {
init();
return;
}
LinkedList temp = new LinkedList();
Iterator keys = fileMap.values().iterator();
while (keys.hasNext()) {
FileSummary next = (FileSummary) keys.next();
File file = next.getFile();
if ((file != null) && !file.exists()) {
temp.add(file);
}
}
Iterator iter = temp.iterator();
while (iter.hasNext()) {
File next = (File) iter.next();
removeFileSummary(next);
}
}
/**
* Remove the file summary for a particular file
*
*@param file the file we are looking up
*/
public static void removeFileSummary(File file)
{
if (fileMap == null) {
init();
}
String key = getKey(file);
FileSummary fileSummary = (FileSummary) fileMap.get(key);
if (fileSummary != null) {
// There is something to remove
fileMap.remove(key);
// Get the parent
PackageSummary parent = (PackageSummary) fileSummary.getParent();
parent.deleteFileSummary(fileSummary);
}
}
/**
* Get the key that is used to index the files
*
*@param file the file we are using to find the key
*@return the key
*/
protected static String getKey(File file)
{
try {
return file.getCanonicalPath();
}
catch (IOException ioe) {
return "Unknown";
}
}
/**
* Registers a single new file. This method is used by the rapid metadata
* reloader
*
*@param summary Description of Parameter
*/
static void register(FileSummary summary)
{
if (fileMap == null) {
init();
}
File file = summary.getFile();
if (file == null) {
return;
}
fileMap.put(getKey(file), summary);
}
/**
* Scans through the tree looking for a type declaration
*
*@param root the root of the abstract syntax tree
*@return true if there is a type node present
*/
private static boolean hasType(SimpleNode root)
{
int last = root.jjtGetNumChildren();
for (int ndx = 0; ndx < last; ndx++) {
if (root.jjtGetChild(ndx) instanceof ASTTypeDeclaration) {
return true;
}
}
return false;
}
/**
* Initialization method
*/
private static void init()
{
if (fileMap == null) {
fileMap = new HashMap();
}
}
/**
* Description of the Method
*
*@param file Description of Parameter
*@return Description of the Returned Value
*/
private static SummaryLoaderState loadNewFileSummary(File file)
{
// Create a new file summary object
ParserFactory factory = new FileParserFactory(file);
SimpleNode root = factory.getAbstractSyntaxTree(false);
if (root == null) {
return null;
}
// Start the summary
SummaryLoaderState state = new SummaryLoaderState();
state.setFile(file);
root.jjtAccept(new SummaryLoadVisitor(), state);
return state;
}
/**
* Description of the Method
*
*@param state Description of Parameter
*@param file Description of Parameter
*@return Description of the Returned Value
*/
private static FileSummary linkFileSummary(SummaryLoaderState state, File file)
{
FileSummary result;
// Associate them together
result = (FileSummary) state.getCurrentSummary();
((PackageSummary) result.getParent()).addFileSummary(result);
// Store it away
fileMap.put(getKey(file), result);
return result;
}
/**
* Description of the Method
*
*@param file Description of Parameter
*@param result Description of Parameter
*@param root Description of Parameter
*/
private static void reloadFileSummary(File file, FileSummary result, SimpleNode root)
{
SummaryLoaderState state = new SummaryLoaderState();
state.setFile(file);
state.startSummary(result);
state.setCode(SummaryLoaderState.LOAD_FILE);
root.jjtAccept(new SummaryLoadVisitor(), state);
}
/**
* Description of the Method
*
*@param file Description of Parameter
*/
private static void resetFileSummary(File file, FileSummary result)
{
if (result == null) {
return;
}
result.theFile = file;
result.importList = null;
result.typeList = null;
result.isMoving = false;
result.delete = false;
}
/**
* This method allows JBuilder to load a file summary from
* the buffer
*
*@param file the file
*@param input the input stream
*@return the file summary loaded
*/
public static FileSummary reloadFromBuffer(File file, InputStream input) {
if (fileMap == null) {
init();
}
if (file == null) {
System.out.println("No file!");
return null;
}
String key = getKey(file);
if (key == null) {
System.out.println("No key: " + file.toString());
return null;
}
FileSummary result = (FileSummary) fileMap.get(key);
if (result == null) {
System.out.println("No initial file summary");
SummaryLoaderState state = loadNewFileSummary(file);
result = linkFileSummary(state, file);
// If you still can't get something that makes sense abort
if (result == null) {
System.out.println("Unable to load the file summary from the file");
return null;
}
}
resetFileSummary(file, result);
result.lastModified = new Date(file.lastModified());
// Create a new file summary object
ParserFactory factory;
if (input == null) {
factory = new FileParserFactory(file);
}
else {
factory = new InputStreamParserFactory(input, key);
}
SimpleNode root = factory.getAbstractSyntaxTree(false);
if (root == null) {
System.out.println("Unable to get a parse tree for this file: Using existing file summary");
return result;
}
reloadFileSummary(file, result, root);
return result;
}
}